421 stories
·
0 followers

Democrats Join 2024's Graveyard of Incumbents

1 Share
An anonymous reader shares a post from Financial Times: The economic and geopolitical conditions of the past year or two have created arguably the most hostile environment in history for incumbent parties and politicians across the developed world. From America's Democrats to Britain's Tories, Emmanuel's Macron's Ensemble coalition to Japan's Liberal Democrats, even to Narendra Modi's erstwhile dominant BJP, governing parties and leaders have undergone an unprecedented series of reversals this year. The incumbents in every single one of the 10 major countries that have been tracked by the ParlGov global research project and held national elections in 2024 were given a kicking by voters. This is the first time this has ever happened in almost 120 years of records. Ultimately voters don't distinguish between unpleasant things that their leaders and governments have direct control over, and those that are international phenomena resulting from supply-side disruptions caused by a global pandemic or the warmongering of an ageing autocrat halfway across the world. Voters don't like high prices, so they punished the Democrats for being in charge when inflation hit. The cost of living was also the top issue in Britain's July general election and has been front of mind in dozens of other countries for most of the last two years. That different politicians, different parties, different policies and different rhetoric deployed in different countries have all met similar fortunes suggests that a large part of Tuesday's American result was locked in regardless of the messenger or the message. The wide variety of places and people who swung towards Trump also suggests an outcome that was more inevitable than contingent. But it's not just about inflation. An update of economist Arthur Okun's "misery index" -- the sum of the inflation and unemployment rates -- for this era might swap out joblessness and replace it with immigration. On this basis, the past couple of years in the US, UK and dozens of other countries have been characterised by more economic and societal upheaval than they have seen in generations.

Read more of this story at Slashdot.

Read the whole story
Share this story
Delete

The case of a program that crashed on its first instruction

2 Shares

A customer was baffled by crash reports that indicated that their program was failing on its very first instruction.

I opened one of the crash dumps, and it was so weird, the debugger couldn’t even say what went wrong.

ERROR: Unable to find system thread FFFFFFFF
ERROR: The thread being debugged has either exited or cannot be accessed
ERROR: Many commands will not work properly
This dump file has an exception of interest stored in it.
The stored exception information can be accessed via .ecxr.
ERROR: Exception C0000005 occurred on unknown thread FFFFFFFF
(61c.ffffffff): Access violation - code c0000005 (first/second chance not available)
0:???> r
WARNING: The debugger does not have a current process or thread
WARNING: Many commands will not work
       ^ Illegal thread error in 'r'
0:???> .ecxr
WARNING: The debugger does not have a current process or thread
WARNING: Many commands will not work
0:???>

Let’s see what threads we have.

0:???> ~
WARNING: The debugger does not have a current process or thread
WARNING: Many commands will not work
   0  Id: 61c.12b4 Suspend: 1 Teb: 000000c7`9604d000 Unfrozen
   1  Id: 61c.22d4 Suspend: 1 Teb: 000000c7`9604f000 Unfrozen
   2  Id: 61c.1ab0 Suspend: 1 Teb: 000000c7`96051000 Unfrozen
   3  Id: 61c.3308 Suspend: 1 Teb: 000000c7`96053000 Unfrozen
   4  Id: 61c.2af0 Suspend: 1 Teb: 000000c7`96055000 Unfrozen
   5  Id: 61c.2054 Suspend: 1 Teb: 000000c7`96059000 Unfrozen
0:???>

I wonder what they are doing.

We’ll switch to each thread just to see what instruction they are at

0:???> ~0s
WARNING: The debugger does not have a current process or thread
WARNING: Many commands will not work
ntdll!RtlUserThreadStart:
00007ffa`bb16df50 4883ec78        sub     rsp,78h
0:000> ~*s
         ^ Illegal thread error in '~*s'
0:000> ~1s
00000293`42074058 66894340        mov     word ptr [rbx+40h],ax ds:00007ff6`e4600040=1f0e
0:001> ~2s
ntdll!ZwWaitForWorkViaWorkerFactory+0x14:
00007ffa`bb1b29c4 c3              ret
0:002> ~3s
ntdll!ZwWaitForWorkViaWorkerFactory+0x14:
00007ffa`bb1b29c4 c3              ret
0:003> ~4s
ntdll!ZwWaitForWorkViaWorkerFactory+0x14:
00007ffa`bb1b29c4 c3              ret
0:004> ~5s
ntdll!ZwDelayExecution+0x14:
00007ffa`bb1af3f4 c3              ret

The ostensible reason for the crash was an invalid write instruction, and only thread 1 is doing a write. Let’s take a closer look at what it’s trying to write to.

0:001> !address @rbx

Usage:                  Image
Base Address:           00007ff6`e4600000
End Address:            00007ff6`e4601000
Region Size:            00000000`00001000 (   4.000 kB)
State:                  00001000          MEM_COMMIT
Protect:                00000002          PAGE_READONLY
Type:                   01000000          MEM_IMAGE
Allocation Base:        00007ff6`e4600000
Allocation Protect:     00000080          PAGE_EXECUTE_WRITECOPY
Image Path:             C:\Program Files\Contoso\ContosoDeluxe.exe
Module Name:            ContosoDeluxe
Loaded Image Name:      ContosoDeluxe.exe
Mapped Image Name:      C:\Program Files\Contoso\ContosoDeluxe.exe
More info:              lmv m ContosoDeluxe
More info:              !lmi ContosoDeluxe
More info:              ln 0x7ff6e4600000
More info:              !dh 0x7ff6e4600000

Content source: 2 (mapped), length: 400
0:001> ln @rbx
(00000000`00000000)   ContosoDeluxe!__ImageBase

Okay, so we are writing to the mapped image header for ContosoDeluxe itself. This is a read-only page (PAGE_READ­ONLY), which is why we take a write access violation.

In fact, we’re writing into the image header, which is not something anybody normally does. This looks quite suspicious.

If we ask for stacks, we get this:

0:001> ~*k

   0  Id: 61c.12b4 Suspend: 1 Teb: 000000c7`9604d000 Unfrozen
Child-SP          RetAddr               Call Site
000000c7`962ffd48 00000000`00000000     ntdll!RtlUserThreadStart

   1  Id: 61c.22d4 Suspend: 1 Teb: 000000c7`9604f000 Unfrozen
Child-SP          RetAddr               Call Site
000000c7`963ff900 00007ff6`e4600000     0x00000293`42074058

   2  Id: 61c.1ab0 Suspend: 1 Teb: 000000c7`96051000 Unfrozen
Child-SP          RetAddr               Call Site
000000c7`964ff718 00007ffa`bb145a0e     ntdll!ZwWaitForWorkViaWorkerFactory+0x14
000000c7`964ff720 00007ffa`ba25244d     ntdll!TppWorkerThread+0x2ee
000000c7`964ffa00 00007ffa`bb16df78     kernel32!BaseThreadInitThunk+0x1d
000000c7`964ffa30 00000000`00000000     ntdll!RtlUserThreadStart+0x28

   3  Id: 61c.3308 Suspend: 1 Teb: 000000c7`96053000 Unfrozen
Child-SP          RetAddr               Call Site
000000c7`965ff6a8 00007ffa`bb145a0e     ntdll!ZwWaitForWorkViaWorkerFactory+0x14
000000c7`965ff6b0 00007ffa`ba25244d     ntdll!TppWorkerThread+0x2ee
000000c7`965ff990 00007ffa`bb16df78     kernel32!BaseThreadInitThunk+0x1d
000000c7`965ff9c0 00000000`00000000     ntdll!RtlUserThreadStart+0x28

   4  Id: 61c.2af0 Suspend: 1 Teb: 000000c7`96055000 Unfrozen
Child-SP          RetAddr               Call Site
000000c7`966ffad8 00007ffa`bb145a0e     ntdll!ZwWaitForWorkViaWorkerFactory+0x14
000000c7`966ffae0 00007ffa`ba25244d     ntdll!TppWorkerThread+0x2ee
000000c7`966ffdc0 00007ffa`bb16df78     kernel32!BaseThreadInitThunk+0x1d
000000c7`966ffdf0 00000000`00000000     ntdll!RtlUserThreadStart+0x28

   5  Id: 61c.2054 Suspend: 1 Teb: 000000c7`96059000 Unfrozen
Child-SP          RetAddr               Call Site
000000c7`968ffcb8 00007ffa`bb165833     ntdll!ZwDelayExecution+0x14
000000c7`968ffcc0 00007ffa`b88f9fcd     ntdll!RtlDelayExecution+0x43
000000c7`968ffcf0 00000293`420a1efd     KERNELBASE!SleepEx+0x7d
000000c7`968ffd70 00000000`00000000     0x00000293`420a1efd

Thread 1 is the suspicious thread that committed the access violation.

There’s another suspicious thread, thread 5, which is in a SleepEx call called from the same suspicious source 0x00000293`420xxxxx. This other thread is probably waiting for something to happen, so let’s take a look at it.

First, let’s see what kind of memory we are executing from.

0:001> !address 00000293`420a1ee0

Usage:                  <unknown>
Base Address:           00000293`420a0000
End Address:            00000293`420ca000
Region Size:            00000000`0002a000 ( 168.000 kB)
State:                  00001000          MEM_COMMIT
Protect:                00000040          PAGE_EXECUTE_READWRITE
Type:                   00020000          MEM_PRIVATE
Allocation Base:        00000293`420a0000
Allocation Protect:     00000040          PAGE_EXECUTE_READWRITE

Yikes, PAGE_EXECUTE_READ­WRITE. That’s not a good sign. That smells like malicious code injection, because it is highly unusual for normal code to be read-write. But let’s hold out hope that maybe there’s a legitimate explanation for all of this, and it’s just a matter of finding it.

Let’s see what code we are executing.

00000293`420a1ed9 add     rsp,30h
00000293`420a1edd pop     rdi
00000293`420a1ede ret
00000293`420a1edf int     3
00000293`420a1ee0 push    rbx
00000293`420a1ee2 sub     rsp,20h
00000293`420a1ee6 call    00000293`420a13e0
00000293`420a1eeb mov     qword ptr [00000293`420c0c78],rax
00000293`420a1ef2 mov     ecx,3E8h
00000293`420a1ef7 call    qword ptr [00000293`420b4028]
                  ^^^^^^^^ YOU ARE HERE
00000293`420a1efd call    00000293`420a13e0 // do it again
00000293`420a1f02 mov     rdx,rax
00000293`420a1f05 mov     rbx,rax
00000293`420a1f08 call    00000293`420a19d0
00000293`420a1f0d test    eax,eax
00000293`420a1f0f jne     00000293`420a1f22
00000293`420a1f11 mov     rax,qword ptr [00000293`420c0c78]
00000293`420a1f18 mov     qword ptr [00000293`420c0c78],rbx
00000293`420a1f1f mov     rbx,rax
00000293`420a1f22 mov     rcx,rbx
00000293`420a1f25 call    00000293`420a17f0
00000293`420a1f2a jmp     00000293`420a1ef2

The first few instructions, up to the int 3 appear to be the end of the previous function, so we can start our analysis at the push rbx.

    push rbx                        ; preserve register
    sub rsp, 20h                    ; stack frame
    call 00000293`420a13e0          ; mystery function 1
    mov  [00000293`420c0c78],rax    ; save answer in global

00000293`420a1ef2:
    mov  ecx, 3E8h                  ; decimal 1000
    call [00000293`420b4028]        ; mystery function 2
    ^^^^^^^^ YOU ARE HERE

    call 00000293`420a13e0          ; mystery function 1
    mov  rdx, rax                   ; return value becomes param1
    mov  rbx, rax                   ; save return value in rbx
    call 00000293`420a19d0          ; mystery function 3
    test eax,eax                    ; Q: did it succeed?
    jne  00000293`420a1f22          ; N: Skip
    mov  rax, [00000293`420c0c78]   ; get previous value
    mov  [00000293`420c0c78], rbx   ; replace with new value
    mov  rbx, rax                   ; save previous value in rbx

00000293`420a1f22:
    mov   rcx, rbx                  ; rcx = updated value in rbx
    call    00000293`420a17f0       ; mystery function 3
    jmp     00000293`420a1ef2       ; loop back forever

One thing that’s apparent here is that this thread never exits. It’s an infinite loop.

First, let’s see if we can identify the mystery functions.

The easiest is probably mystery function 2, since it looks like a call to an imported function.

0:001> dps 00000293`420b4028 L1
00000293`420b4028  00007ffa`ba258370 kernel32!SleepStub

Aha, mystery function 2 is Sleep, and the call is a Sleep(1000). Which we sort of knew from the stack trace but it’s nice to see confirmation.

But let’s look around near that address, since that may be part of a larger table of function pointers.

00000293`420b4000  00007ffa`baa59810 advapi32!RegCloseKeyStub
00000293`420b4008  00007ffa`baa596e0 advapi32!RegQueryInfoKeyWStub
00000293`420b4010  00007ffa`baa595a0 advapi32!RegOpenKeyExWStub 
00000293`420b4018  00007ffa`baa5ab30 advapi32!RegEnumValueWStub
00000293`420b4020  00000000`00000000
00000293`420b4028  00007ffa`ba258370 kernel32!SleepStub
00000293`420b4030  00007ffa`ba250cc0 kernel32!GetLastErrorStub
00000293`420b4038  00007ffa`ba266b60 kernel32!lstrcatW
00000293`420b4040  00007ffa`ba25ff00 kernel32!CloseHandle
00000293`420b4048  00007ffa`ba254380 kernel32!CreateThreadStub

Bingo, this appears to be a table of imported function pointers.

Mystery function 1 seems to be called to start things off, and then again in a loop, so it seems kind of important. Let’s see what it is.

00000293`420a13e0 mov     qword ptr [rsp+8],rbx
00000293`420a13e5 mov     qword ptr [rsp+10h],rsi
00000293`420a13ea mov     qword ptr [rsp+18h],rdi
00000293`420a13ef push    rbp
00000293`420a13f0 mov     rbp,rsp
00000293`420a13f3 sub     rsp,80h
00000293`420a13fa mov     rax,qword ptr [00000293`420bf010]
00000293`420a1401 xor     rax,rsp
00000293`420a1404 mov     qword ptr [rbp-8],rax
00000293`420a1408 mov     ecx,40h
00000293`420a140d call    00000293`420a8478 // mystery function 3

This looks like a typical C function, not hand-coded assembly. After saving non-volatile registers, it builds a stack frame, and the mov rax, [global] followed by a xor rax, rsp looks a lot like a /GS stack canary.

So at least it’s nice that this rogue code was compiled with stack buffer overflow protection. Can’t be too careful.

Let’s look at mystery function 3.

00000293`420a8478
    push rbx
    sub  rsp, 20h
    mov  rbx, rcx
    jmp  00000293`420a8492

00000293`420a8483
    mov  rcx, rbx
    call 00000293`420aad50
    test eax, eax
    je   00000293`420a84a2
    mov  rcx, rbx

00000293`420a8492
    call 00000293`420aadb4
    test rax, rax
    je   00000293`420a8483
    add  rsp, 20h
    pop  rbx
    ret

00000293`420a84a2
    cmp  rbx, 0FFFFFFFFFFFFFFFFh
    je   00000293`420a84ae

    call 00000293`420a8c80
    int  3

00000293`420a84ae
    call 00000293`420a8ca0
    int  3

00000293`420a84b4
    jmp  00000293`420a8478

This reverse-compiles to

uint64_t something(uint64_t value)
{
    uint64_t p;
    while (uint64_t p = func00000293420aadb4(value); !p) {
        if (!func00000293420aad50(value)) {
            if (value == ~0ULL) {
                func00000293420a8c80();
            } else {
                func00000293420a8c80();
            }
            // NOTREACHED
        }
    }
    return p;
}

This seems to call a function at func00000293420aadb4 repeatedly.

00000293`420aadb4 jmp     00000293`420acf8c

This appears to be an incremental linking thunk. So whatever this is, it looks like it was compiled in debug mode.

00000293`420acf8c
    push rbx
    sub  rsp, 20h
    mov  rbx,rcx
    cmp  rcx, 0FFFFFFFFFFFFFFE0h
    ja   00000293`420acfd7
    test rcx, rcx
    mov  eax, 1
    cmove rbx, rax
    jmp  00000293`420acfbe

00000293`420acfa9
    call 00000293`420b02c0
    test eax, eax
    je   00000293`420acfd7
    mov  rcx, rbx
    call 00000293`420aad50
    test eax, eax
    je   00000293`420acfd7

00000293`420acfbe 
    mov  rcx, [00000293`420c07f8]
    mov  r8, rbx
    xor  edx, edx
    call [00000293`420b4298]
    test rax, rax
    je   00000293`420acfa9
    jmp  00000293`420acfe4

00000293`420acfd7
    call  00000293`420ac71c
    mov   [rax], 0Ch
    xor   eax, eax
    add   rsp, 20h
    pop   rbx
    ret

The initial comparison against 0xFFFFFFFF`FFFFFFFE makes me suspect that this is malloc() or operator new because those functions begin with a check for an excessive allocation size, to avoid integer overflow.

And indeed, that’s basically what this function is, as revealed by the indirect function call:

0:005> dps 00000293`420b4298 L1
00000293`420b4298  00007ffa`bb14cca0 ntdll!RtlAllocateHeap

Okay, so we found malloc() or operator new.

This will help us understand mystery function 1 a lot better.

00000293`420a13e0
    mov     [rsp+8], rbx
    mov     [rsp+10h], rsi
    mov     [rsp+18h], rdi
    push    rbp
    mov     rbp, rsp
    sub     rsp, 80h
    mov     rax, [00000293`420bf010]
    xor     rax, rsp
    mov     [rbp-8], rax      ; /GS canary
    mov     ecx, 40h
    call    00000293`420a8478 ; allocate 64 bytes
    xorps   xmm0, xmm0
    mov     ecx, 18h
    mov     rdi,rax           ; save first allocation
    movups  [rax],xmm0        ; zero out first allocation
    movups  [rax+10h],xmm0
    movups  [rax+20h],xmm0
    movups  [rax+30h],xmm0
    call    00000293`420a8478 ; allocate 24 bytes
    xor     esi,esi
    mov     ecx, 80h
    mov     rbx,rax           ; save second allocation
    mov     [rax+0Ch], rsi    ; zero out second allocation
    mov     [rax+14h], esi
    mov     [rax], esi
    mov     [rax+4], 10h
    mov     [rax+8], 1
    call    00000293`420a84b4 ; mystery function 4
    mov     [rbx+10h], rax    ; save result
    lea     ecx, [rsi+10h]    ; ecx = 0x10
    mov     [rdi], rbx
    call    00000293`420a8478 ; third allocation
    lea     ecx, [rsi+40h]    ; ecx = 0x40
    mov     rbx, rax
    mov     [rax+8], rsi      ; initialize third allocation
    mov     [rax], esi
    mov     [rax+4], 10h
    call    00000293`420a84b4 ; mystery function 4
    mov     [rbx+8], rax
    lea     ecx, [rsi+18h]    ; ecx = 0x18

Okay, so this function starts by allocating many memory blocks and initializing them.

Let’s skip ahead to where it finally does something interesting.

    lea     rdx, [00000293`420bba90] ; LR"(SOFTWARE\systemconfig)"
    lea     rax, [rbp-50h]
    mov     [rdi+38h], rbx
    mov     r9d, 20119h       ; KEY_READ
    mov     [rsp+20h], rax
    xor     r8d, r8d
    mov     rcx,0FFFFFFFF80000002h ; HKEY_LOCAL_MACHINE
    call    qword ptr [00000293`420b4010] ; RegOpenKeyExW
    test    eax, eax

A dps 00000293`420b4010 reveals that the function pointer is Reg­Open­Key­ExW, so the entire function call must have been

RegOpenKeyExW(HKEY_LOCAL_MACHINE,
    L"SOFTWARE\\systemconfig", 0, KEY_READ, &key);

Further disassembly shows that if the code successfully opens the key, it tries to read some values from it. My guess is that system­config is where the code stores its state.

Okay, so maybe I can speed things up by dumping strings and seeing if there’s anything that will give me a clue about the identity of this code. Recall that the !address command told us that the memory block was

0:001> !address 00000293`420a1ee0
Base Address:           00000293`420a0000
End Address:            00000293`420ca000

We’ll ask the !mex debugger extension to find any strings in the memory block.

0:005> !mex.strings 00000293`420a0000 00000293`420ca000
...
00000293420bbd10 system
00000293420bc1d4 H:\rootkit\r77-rootkit-master\vs\x64\Release\r77-x64.pdb

Okay, so I guess it’s malware, or at least self-identifies as a rootkit. And, hey, an Internet search for this rootkit name shows that its source code is public.

The good news for the developer is that the problem is not their fault. The bad news is that since the crash dumps are submitted anonymously, they have no way of contacting the users to tell them that they have been infected with malware.

The post The case of a program that crashed on its first instruction appeared first on The Old New Thing.

Read the whole story
Share this story
Delete

The making of Bring Back Plus/Minus

2 Shares

A lesson about the software forensics process involved in developing the Bring Back Plus/Minus extension, which brings back the plus/minus symbols to the editor outlining feature in Visual Studio 2022.

While I did have the advantage of being able to look at the Visual Studio source code, I could have figured this out just as well without it, and that’s the focus of this article.

The investigation of how to bring back the plus/minus symbols started with this important clue from a comment on the Developer Community feedback ticket for this issue:

However, Visual Studio has a very rich extensibility model, and if users have strong feelings about the visuals in their IDE, I encourage people to try writing an extension to change the icon used here. The class that controls the expansion is this one: OutliningMarginHeaderControl

A quick internet search for the class name led me to this reference article for OutliningMarginHeaderControl Class where the definition contains the following valuable information:

  • Namespace: Microsoft.VisualStudio.Text.Editor
  • Assembly: Microsoft.VisualStudio.Text.UI.Wpf.dll
  • Package: Microsoft.VisualStudio.Text.UI.Wpf v17.9.187

Now that we know where to find this class, it’s time to examine the assembly. For that we will use the excellent ILSpy tool, which you can install from the Microsoft Store: ILSpy Fresh

Once you’ve installed ILSpy, launch it and load the assembly Microsoft.VisualStudio.Text.UI.Wpf.dll. You can find this assembly in the Visual Studio installation folder, usually in the following path:

<VSInstallDir>\Common7\IDE\CommonExtensions\Microsoft\Editor\Microsoft.VisualStudio.Text.UI.Wpf.dll

After the assembly is loaded, search for the class OutliningMarginHeaderControl in the Microsoft.VisualStudio.Text.Editor namespace and you will find the following code for the static constructor:

OutliningMarginHeaderControl in ILSpy

This is where the default style key for the control is being set. The next step is to find the XAML Style for the control. For that we open the Resources node where we find themes/generic.baml, which includes the entire style for the OutliningMarginHeaderControl:

generic.baml node in ILSpy


<Style x:Key="{x:Type textUiWpf:OutliningMarginHeaderControl}" TargetType="{x:Type textUiWpf:OutliningMarginHeaderControl}">
    <Style.Resources> 
      <ResourceDictionary> 
        <Geometry x:Key="ExpandRight">F1M2.146.146a.5.5,0,0,1,.708,0l4,4a.5.5,0,0,1,0,.708l-4,4a.5.5,0,0,1-.708-.708L5.793,4.5,2.146.854A.5.5,0,0,1,2.146.146Z</Geometry> 
        <Geometry x:Key="ExpandDown">F1M8.854,2.146a.5.5,0,0,1,0,.708l-4,4a.5.5,0,0,1-.708,0l-4-4a.5.5,0,0,1,.708-.708L4.5,5.793,8.146,2.146A.5.5,0,0,1,8.854,2.146Z</Geometry> 
      </ResourceDictionary> 
    </Style.Resources> 
    <Setter Property="Focusable" Value="False" /> 
    <Setter Property="FrameworkElement.Cursor" Value="Hand" /> 
    <Setter Property="Template"> 
      <Setter.Value> 
        <ControlTemplate TargetType="{x:Type textUiWpf:OutliningMarginHeaderControl}"> 
          <Grid> 
            <Viewbox Name="ExpandCollapseIcon" Width="9" Height="13" VerticalAlignment="Center"> 
              <Border Width="9" Height="13" Background="{DynamicResource outlining.chevron.background}"> 
                <Rectangle Name="ExpandCollapseRectangle" Width="9" Height="9"> 
                  <FrameworkElement.Resources> 
                    <ResourceDictionary> 
                      <SolidColorBrush x:Key="canvas" Opacity="0" /> 
                    </ResourceDictionary> 
                  </FrameworkElement.Resources> 
                  <Shape.Fill> 
                    <DrawingBrush Stretch="None"> 
                      <DrawingBrush.Drawing> 
                        <DrawingGroup> 
                          <DrawingGroup> 
                            <GeometryDrawing Brush="{DynamicResource canvas}" Geometry="F1 M9,0 L9,9 L0,9 L0,0" /> 
                          </DrawingGroup> 
                          <DrawingGroup> 
                            <GeometryDrawing Brush="{DynamicResource outlining.chevron.foreground}" Geometry="{StaticResource ExpandRight}" /> 
                          </DrawingGroup> 
                        </DrawingGroup> 
                      </DrawingBrush.Drawing> 
                    </DrawingBrush> 
                  </Shape.Fill> 
                </Rectangle> 
              </Border> 
            </Viewbox> 
          </Grid> 
          <ControlTemplate.Triggers> 
            <Trigger Property="textUiWpf:OutliningMarginHeaderControl.IsExpanded" Value="True"> 
              <Setter TargetName="ExpandCollapseRectangle" Property="Shape.Fill"> 
                <Setter.Value> 
                  <DrawingBrush Stretch="None"> 
                    <DrawingBrush.Drawing> 
                      <DrawingGroup> 
                        <DrawingGroup> 
                          <GeometryDrawing Brush="{DynamicResource canvas}" Geometry="F1 M9,0 L9,9 L0,9 L0,0" /> 
                        </DrawingGroup> 
                        <DrawingGroup> 
                          <GeometryDrawing Brush="{DynamicResource outlining.chevron.foreground}" Geometry="{StaticResource ExpandDown}" /> 
                        </DrawingGroup> 
                      </DrawingGroup> 
                    </DrawingBrush.Drawing> 
                  </DrawingBrush> 
                </Setter.Value> 
              </Setter> 
            </Trigger> 
          </ControlTemplate.Triggers> 
        </ControlTemplate> 
      </Setter.Value> 
    </Setter> 
</Style> 

This is the style for the new chevron symbols. Here we find interesting things like the geometry for the expand/collapse symbols, the colors, and the trigger that changes the symbol when the control is expanded. The next step is to find the original Style for the plus/minus symbols. For that we can either use an older VS installation or we can take advantage of the reference information above, which states that this control is included in the Microsoft.VisualStudio.Text.UI.Wpf v17.9.187 NuGet package. Now we know that the symbols changed in 17.9, so we will look for a version before that. For example, the last one before that is 17.8.222.

We can download the package, change its extension from .nupkg to .zip, and extract the assembly from the lib\net472 folder. Once we have the assembly, we can load it in ILSpy and look for the original style for the control just like we did before:


<Style x:Key="{x:Type textUiWpf:OutliningMarginHeaderControl}" TargetType="{x:Type textUiWpf:OutliningMarginHeaderControl}">
	<Setter Property="Focusable" Value="False" />
	<Setter Property="Template">
		<Setter.Value>
			<ControlTemplate TargetType="{x:Type textUiWpf:OutliningMarginHeaderControl}">
				<Grid>
					<Border Name="WhitePadding" Height="11" Width="9" BorderBrush="{DynamicResource ViewBackgroundBrush}" Background="{DynamicResource ViewBackgroundBrush}" BorderThickness="0,1,0,1" VerticalAlignment="Center">
						<Border Name="TheSquare" Height="9" Width="9" BorderBrush="{DynamicResource outlining.verticalrule.foreground}" Background="{DynamicResource outlining.square.background}" BorderThickness="1">
							<Canvas>
								<Line X1="1" Y1="3.5" X2="6" Y2="3.5" Stroke="{DynamicResource outlining.square.foreground}" />
								<Line Name="Vertical" X1="3.5" Y1="1" X2="3.5" Y2="6" Stroke="{DynamicResource outlining.square.foreground}" />
							</Canvas>
						</Border>
					</Border>
				</Grid>
				<ControlTemplate.Triggers>
					<Trigger Property="textUiWpf:OutliningMarginHeaderControl.IsExpanded" Value="True">
						<Setter TargetName="Vertical" Property="Visibility" Value="Hidden" />
						<Setter TargetName="TheSquare" Value="{DynamicResource ViewBackgroundBrush}" Property="Border.Background" />
					</Trigger>
				</ControlTemplate.Triggers>
			</ControlTemplate>
		</Setter.Value>
	</Setter>
</Style>

Since the new style no longer uses dynamic resources for outlining.square, we must assume that these resources are no longer available in the new version of VS. This means that we needed to replace these resources with other colors. For the foreground outlining.chevron.foreground made sense. The background was a little bit harder because outlining.chevron.background didn’t do anything. With a little experimenting I had settled on outlining.collapsehintadornment.background for the first version of the extension. Both were not ideal or what they used to be – the chevron foreground is quite a bit darker and the background for the plus symbol is a lot lighter than before, but this combination worked in both light and dark themes, and any other existing color resources would look wrong in one theme or another.

How to build a VSIX extension to override the style

To override the style, I started by creating a new VSIX project with an async package:

New VSIX Project Template

Then I added a new ResourceDictionary file Style.xaml to the project and copied the style from the old assembly, pasted it into the ResourceDictionary, and updated it with the new color resource value. Make sure that the Build Action for this Style.xaml is set to Page. I also gave this style a key x:Key="OriginalOutliningMarginHeaderControlStyle", so that I can easily look it up later:


<ResourceDictionary
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:textUiWpf="clr-namespace:Microsoft.VisualStudio.Text.Editor;assembly=Microsoft.VisualStudio.Text.UI.Wpf">
	<Style x:Key="OriginalOutliningMarginHeaderControlStyle" TargetType="textUiWpf:OutliningMarginHeaderControl">
		...

The last thing that needed to be done was to override the style in the InitializeAsync method of the package. The following code loads the resource dictionary, looks up the style by its key, and places it in the current application’s resources using the implicit style key typeof(OutliningMarginHeaderControl), so that Visual Studio will find this Style first:


protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) 
{ 
	// When initialized asynchronously, the current thread may be a background thread at this point. 
	// Do any initialization that requires the UI thread after switching to the UI thread. 
	await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); 
	var resourceDictionary = new ResourceDictionary(); 
	resourceDictionary.Source = new Uri("pack://application:,,,/BringBackPlusMinus;component/Style.xaml"); 
	var style = resourceDictionary["OriginalOutliningMarginHeaderControlStyle"]; 
	Application.Current.Resources[typeof(OutliningMarginHeaderControl)] = style; 
}

Building and running the project launched the experimental instance of Visual Studio and voila – the plus/minus symbols were back!

Now all that was left to do was to update the vsixmanifest, build the extension in Release mode, and upload it to the Visual Studio Marketplace following this Walkthrough: Publish a Visual Studio extension.

Adding support for color customization

When I started using the preview builds of Visual Studio 2022 17.12, I noticed that the rendering of the plus/minus symbols was broken because the chevron symbol color definition had changed. I decided to introduce a dedicated color definition for the plus/minus symbols to avoid such problems going forward:


[Export(typeof(EditorFormatDefinition))] 
[Name(OutliningExpanderIdentifier)] 
[UserVisible(true)] 
internal sealed class OutliningExpanderFormatDefinition : EditorFormatDefinition 
{ 
	public const string OutliningExpanderIdentifier = "outlining.plusminus"; 
	public OutliningExpanderFormatDefinition() 
	{ 
		this.ForegroundColor = Color.FromRgb(0x55, 0x55, 0x55); 
		this.BackgroundColor = Color.FromRgb(0xE2, 0xE2, 0xE2); 
		this.DisplayName = Strings.OutliningMarginPlusMinus; 
	} 
}

As an added benefit the colors are now consistent with the original plus/minus symbols, and they can be customized in Tools -> Options -> Environment -> Fonts and Colors:

Plus/minus symbol colors in Tools/Options dialog

To make sure that the colors look correct in the dark theme as well, I added a ThemeColors.xml file to the project with the following content:


<Themes> 
	<Theme Name="Dark" GUID="{1ded0138-47ce-435e-84ef-9ec1f439b749}"> 
		<Category Name="BringBackPlusMinus" GUID="{063E9575-C1A8-4729-BB15-AAA2EFB44FC0}"> 
			<Color Name="outlining.plusminus"> 
				<Background Type="CT_RAW" Source="FF000000" /> 
				<Foreground Type="CT_RAW" Source="FFE2E2E2" /> 
			</Color> 
		</Category> 
	</Theme> 
</Themes>

It needs to be converted to a .pkgdef file using the VsixColorCompiler first, which is then included in the extension’s .vsixmanifest.

Conclusion

In summary, with a little bit of detective work we were able to locate the style for the outlining symbols, build a VSIX extension, and override the style with the original style that brings back the original plus/minus outlining buttons.

The source code for the extension is available on GitHub.

The post The making of Bring Back Plus/Minus appeared first on Visual Studio Blog.

Read the whole story
Share this story
Delete

On locale-aware substring matching, either case-sensitive or case-insensitive

2 Shares

Say you want to do a case-insensitive substring search in a locale-aware manner. For example, maybe you have a list of names, and you want the user to be able to search for them by typing any name fragment. A search for “ber” could find “Bert” as well as “Roberta”.

I’ve seen people solve this problem by converting the string to lowercase, and then doing a code unit-based substring search. This technique doesn’t work for multiple reasons.

One reason is that some languages (like English) do not consider diacritics significant in collation. The word naive and naïve are considered equivalent for searching purposes. But a code unit substring search considers them different.

For languages in which diacritics are significant, you have the problem of composed and decomposed characters. For example, the lowercase a with ring in the Swedish word någon could be represented either as

  • Two code points: U+0061 (LATIN SMALL LETTER A) followed by U+030A (COMBINING RING ABOVE), or
  • A single code point: U+03E5 (LATIN SMALL LETTER A WITH RING ABOVE)

The number of possibilities increases if you have characters with multiple diacritics. And then you also have ligatures, where the “fi” ligature is equivalent to two separate characters f and i.

So what’s the right thing to do?

In Windows, you can use the Find­NLS­String­Ex function to do a locale-aware substring search. Use the LINGUISTIC_IGNORE­DIACRITIC flag to say that you want to honor diacritics only when they are significant to the locale.¹ (A better name would have been LINGUISTIC_IGNORE­INSIGNIFICANT­DIACRITICS.)

On other platforms, and even on Windows,² you can use the ICU library’s string search service and search with primary weight. (Primary weight honors diacritics which are significant to the locale.)

Bonus reading: A popular but wrong way to convert a string to uppercase or lowercase. What has case distinction but is neither uppercase nor lowercase?

¹ Throw in one of the IGNORE­CASE flags if you want a case-sensitive substring search.

² The Windows globalization team now recommends that people use ICU, which has been part of Windows since Windows 10 version 1703 (build 15063). More details and gotchas here.

The post On locale-aware substring matching, either case-sensitive or case-insensitive appeared first on The Old New Thing.

Read the whole story
Share this story
Delete

More dentists are pulling healthy teeth to sell pricy implants, experts warn

1 Share

Becky Carroll was missing a few teeth, others were stained or crooked. Ashamed, she smiled with lips pressed closed. Her dentist offered to fix most of her teeth with root canals and crowns, Carroll said, but she was wary of traveling a long road of dental work.

Then Carroll saw a TV commercial for another path: ClearChoice Dental Implant Centers. The company advertises that it can give patients “a new smile in as little as one day” by surgically replacing teeth instead of fixing them.

So Carroll saved and borrowed for the surgery, she said. In an interview and a lawsuit, Carroll said that at a ClearChoice clinic in New Jersey in 2021, she agreed to pay $31,000 to replace all her natural upper teeth with pearly white prosthetic ones. What came next, Carroll said, was “like a horror movie.”

Carroll alleged that her anesthesia wore off during implant surgery, so she became conscious as her teeth were removed and titanium screws were twisted into her jawbone. Afterward, Carroll’s prosthetic teeth were so misaligned that she was largely unable to chew for more than two years until she could afford corrective surgery at another clinic, according to a sworn deposition from her lawsuit.

ClearChoice has denied Carroll’s claims of malpractice and negligence in court filings and did not respond to requests for comment on the ongoing case.

“I thought implants would be easier, and all at once, so you didn’t have to keep going back to the dentist,” Carroll, 52, said in an interview. “But I should have asked more questions … like, Can they save these teeth?”

Dental implants have been used for more than half a century to surgically replace missing or damaged teeth with artificial duplicates, often with picture-perfect results. While implant dentistry was once the domain of a small group of highly trained dentists and specialists, tens of thousands of dental providers now offer the surgery and place millions of implants each year in the US.

Amid this booming industry, some implant experts worry that many dentists are losing sight of dentistry’s fundamental goal of preserving natural teeth and have become too willing to remove teeth to make room for expensive implants, according to a months-long investigation by KFF Health News and CBS News. In interviews, 10 experts said they had each given second opinions to multiple patients who had been recommended for mouths full of implants that the experts ultimately determined were not necessary. Separately, lawsuits filed across the country have alleged that implant patients like Carroll have experienced painful complications that have required corrective surgery, while other lawsuits alleged dentists at some implant clinics have persuaded, pressured, or forced patients to remove teeth unnecessarily.

The experts warn that implants, for a single tooth or an entire mouth, expose patients to costs and surgery complications, plus a new risk of future dental problems with fewer treatment options because their natural teeth are forever gone.

“There are many cases where teeth, they’re perfectly fine, and they’re being removed unnecessarily,” said William Giannobile, dean of the Harvard School of Dental Medicine. “I really hate to say it, but many of them are doing it because these procedures, from a monetary standpoint, they’re much more beneficial to the practitioner.”

Giannobile and nine other experts say they are combating a false public perception that implants are more durable and longer-lasting than natural teeth, which some believe stems in part from advertising on TV and social media. Implants require upkeep, and although they can’t get cavities, studies have shown that patients can be susceptible to infections in the gums and bone around their implants.

“Just because somebody can afford implants doesn’t necessarily mean that they’re a good candidate,” said George Mandelaris, a Chicago-area periodontist and member of the American Academy of Periodontology Board of Trustees. “When an implant has infection, or when an implant has bone loss, an implant dies a much quicker death than do teeth.”

In its simplest form, implant surgery involves extracting a single tooth and replacing it with a metal post that is screwed into the jaw and then affixed with a prosthetic tooth commonly made of porcelain, also known as a crown. Patients can also use “full-arch” or “All-on-4” implants to replace all their upper or lower teeth—or all their teeth.

For this story, KFF Health News and CBS News sought interviews with large dental chains whose clinics offer implant surgery—ClearChoice, Aspen Dental, Affordable Care, and Dental Care Alliance—each of which declined to be interviewed or did not respond to multiple requests for comment. The Association of Dental Support Organizations, which represents these companies and others like them, also declined an interview request.

ClearChoice, which specializes in full-arch implants, did not answer more than two dozen questions submitted in writing. In an emailed statement, the company said full-arch implants “have become a well-accepted standard of care for patients with severe tooth loss and teeth with poor prognosis.”

“The use of full-arch restorations reflects the evolution of modern dentistry, offering patients a solution that restores their ability to eat, speak, and live comfortably—far beyond what traditional dentures can provide,” the company said.

Carroll said she regrets not letting her dentist try to fix her teeth and rushing to ClearChoice for implants.

“Because it was a nightmare,” she said.

“They are not teeth”

Dental implant surgery can be a godsend for patients with unsalvageable teeth. Several experts said implants can be so transformative that their invention should have contended for a Nobel Prize. And yet, these experts still worry that implants are overused, because it is generally better for patients to have their natural teeth.

Paul Rosen, a Pennsylvania periodontist who said he has worked with implants for more than three decades, said many patients believe a “fallacy” that implants are “bulletproof.”

“You can’t just have an implant placed and go off riding into the sunset,” Rosen said. “In many instances, they need more care than teeth because they are not teeth.”

Generally, a single implant costs a few thousand dollars while full-arch implants cost tens of thousands. Neither procedure is well covered by dental insurance, so many clinics partner with credit companies that offer loans for implant surgeries. At ClearChoice, for example, loans can be as large as $65,000 paid off over 10 years, according to the company’s website.

Despite the price, implants are more popular than ever. Sales increased by more than 6% on average each year since 2010, culminating in more than 3.7 million implants sold in the US in 2022, according to a 2023 report produced by iData Research, a health care market research firm.

Some worry implant dentistry has gone too far. In 10 interviews, dentists and dental specialists with expertise in implants said they had witnessed the overuse of implants firsthand. Each expert said they’d examined multiple patients in recent years who were recommended for full-arch implants by other dentists despite their teeth being treatable with conventional dentistry.

Giannobile, the Harvard dean, said he had given second opinions to “dozens” of patients who were recommended for implants they did not need.

“I see many of these patients now that are coming in and saying, ‘I’ve been seen, and they are telling me to get my entire dentition—all of my teeth—extracted.’ And then I’ll take a look at them and say that we can preserve most of your teeth,” Giannobile said.

Tim Kosinski, who is a representative of the Academy of General Dentistry and said he has placed more than 19,000 implants, said he examines as many as five patients a month who have been recommended for full-arch implants that he deems unnecessary.

“There is a push in the profession to remove teeth that could be saved,” Kosinski said. “But the public isn’t aware.”

Luiz Gonzaga, a periodontist and prosthodontist at the University of Florida, said he, too, had turned away patients who wanted most or all their teeth extracted. Gonzaga said some had received implant recommendations that he considered “an atrocity.”

“You don’t go to the hospital and tell them ‘I broke my finger a couple of times. This is bothering me. Can you please cut my finger off?’ No one will do that,” Gonzaga said. “Why would I extract your tooth because you need a root canal?”

Jaime Lozada, director of an elite dental implant residency program at Loma Linda University, said he’d not only witnessed an increase in dentists extracting “perfectly healthy teeth” but also treated a rash of patients with mouths full of ill-fitting implants that had to be surgically replaced.

Lozada said in August that he’d treated seven such patients in just three months.

“When individuals just make a decision of extracting teeth to make it simple and make money quick, so to speak, that’s where I have a problem,” Lozada said. “And it happens quite often.”

When full-arch implants fail, patients sometimes don’t have enough jawbone left to anchor another set. These patients have little choice but to get implants that reach into cheekbones, said Sohail Saghezchi, an oral and maxillofacial surgeon at the University of California-San Francisco.

“It’s kind of like a last resort,” Saghezchi said. “If those fail, you don’t have anywhere else to go.”

“It was horrendous dentistry”

Most of the experts interviewed for this article said their rising alarm corresponded with big changes in the availability of dental implants. Implants are now offered by more than 70,000 dental providers nationwide, two-thirds of whom are general dentists, according to the iData Research report.

Dentists are not required to learn how to place implants in dental school, nor are they required to complete implant training before performing the surgery in nearly all states. This year, Oregon started requiring dentists to complete 56 hours of hands-on training before placing any implants. Stephen Prisby, executive director of the Oregon Board of Dentistry, said the requirement—the first and only of its kind in the US—was a response to dozens of investigations in the state into botched surgeries and other implant failures, split evenly between general dentists and specialists.

“I was frankly stunned at how bad some of these dentists were practicing,” Prisby said. “It was horrendous dentistry.”

Many dental clinics that offer implants have consolidated into chains owned by private equity firms that have bought out much of implant dentistry. In health care, private equity investment is sometimes criticized for overtreatment and prioritizing short-term profit over patients.

Private equity firms have spent about $5 billion in recent years to buy large dental chains that offer implants at hundreds of clinics owned by individual dentists and dental specialists. ClearChoice was bought for an estimated $1.1 billion in 2020 by Aspen Dental, which is owned by three private equity firms, according to PitchBook, a research firm focused on the private equity industry. Private equity firms also bought Affordable Care, whose largest clinic brand is Affordable Dentures & Implants, for an estimated $2.7 billion in 2021, according to PitchBook. And the private equity wing of the Abu Dhabi government bought Dental Care Alliance, which offers implants at many of its affiliated clinics, for an estimated $1 billion in 2022, according to PitchBook.

ClearChoice and Aspen Dental each said in email statements that the companies’ private equity owners “do not have influence or control over treatment recommendations.” Both companies said dentists or dental specialists make all clinical decisions.

Private equity deals involving dental practices increased ninefold from 2011 to 2021, according to an American Dental Association study published in August. The study also said investors showed an interest in oral surgery, possibly because of the “high prices” of implants.

“Some argue this is a negative thing,” said Marko Vujicic, vice president of the association’s Health Policy Institute, who co-authored the study. “On the other hand, some would argue that involvement of private equity and outside capital brings economies of scale, it brings efficiency.”

Edwin Zinman, a San Francisco dental malpractice attorney and former periodontist who has filed hundreds of dental lawsuits over four decades, said he believed many of the worst fears about private equity owners had already come true in implant dentistry.

“They’ve sold a lot of [implants], and some of it unnecessarily, and too often done negligently, without having the dentists who are doing it have the necessary training and experience,” Zinman said. “It’s for five simple letters: M-O-N-E-Y.”

Hundreds of implant clinics with no specialists

For this article, journalists from KFF Health News and CBS News analyzed the webpages for more than 1,000 clinics in the nation’s largest private equity-owned dental chains, all of which offer some implants. The analysis found that more than 70% of those clinics listed only general dentists on their websites and did not appear to employ the specialists—oral surgeons, periodontists, or prosthodontists—who traditionally have more training with implants.

Affordable Dentures & Implants listed specialists at fewer than 5% of its more than 400 clinics, according to the analysis. The rest were staffed by general dentists, most of whom did not list credentialing from implant training organizations, according to the analysis.

ClearChoice, on the other hand, employs at least one oral surgeon or prosthodontist at each of its more than 100 centers, according to the analysis. But its new parent company, Aspen Dental, which offers implants in many of its more than 1,100 clinics, does not list any specialists at many of those locations.

Not everyone is worried about private equity in implant dentistry. In interviews arranged by the American Academy of Implant Dentistry, which trains dentists to use implants, two other implant experts did not express concerns about private equity firms.

Brian Jackson, a former academy president and implant specialist in New York, said he believed dentists are too ethical and patients are too smart to be pressured by private equity owners “who will never see a patient.”

Jumoke Adedoyin, a chief clinical officer for Affordable Care, who has placed implants at an Affordable Dentures & Implants clinic in the Atlanta suburbs for 15 years, said she had never felt pressure from above to sell implants.

“I’ve actually felt more pressure sometimes from patients who have gone around and been told they need to take their teeth out,” she said. “They come in and, honestly, taking a look at them, maybe they don’t need to take all their teeth out.”

Still, lawsuits filed across the country have alleged that dentists at implant clinics have extracted patients’ teeth unnecessarily.

For example, in Texas, a patient alleged in a 2020 lawsuit that an Affordable Care dentist removed “every single tooth from her mouth when such was not necessary,” then stuffed her mouth with gauze and left her waiting in the lobby as he and his staff left for lunch. In Maryland, a patient alleged in a 2021 lawsuit that ClearChoice “convinced” her to extract “eight healthy upper teeth,” by “greatly downplay[ing] the risks.” In Florida, a patient alleged in a 2023 lawsuit that ClearChoice provided her with no other treatment options before extracting all her teeth, “which was totally unnecessary.”

ClearChoice and Affordable Care denied wrongdoing in their respective lawsuits, then privately settled out of court with each patient. ClearChoice and Affordable Care did not respond to requests for comment submitted to the companies or attorneys. Lawyers for all three plaintiffs declined to comment on these lawsuits or did not respond to requests for comment.

Fred Goldberg, a Maryland dental malpractice attorney who said he has represented at least six clients who sued ClearChoice, said each of his clients agreed to get implants after meeting with a salesperson—not a dentist.

“Every client I’ve had who has gone to ClearChoice has started off meeting a salesperson and actually signing up to get their financing through ClearChoice before they ever meet with a dentist,” Goldberg said. “You meet with a salesperson who sells you on what they like to present as the best choice, which is almost always that they’re going to take out all your natural teeth.”

Becky Carroll, the ClearChoice patient from New Jersey, told a similar story.

Carroll said in her lawsuit that she met first with a ClearChoice salesperson referred to as a “patient education consultant.” In an interview, Carroll said the salesperson encouraged her to borrow money from family members for the surgery and it was not until after she agreed to a loan and passed a credit check that a ClearChoice dentist peered into her mouth.

“It seems way backwards,” Carroll said. “They just want to know you’re approved before you get to talk to a dentist.”

CBS News producer Nicole Keller contributed to this report.

This story originally appeared on KFF Health News, a national newsroom that produces in-depth journalism about health issues and is one of the core operating programs at KFF—an independent source of health policy research, polling, and journalism. Learn more about KFF.

Read full article

Comments



Read the whole story
Share this story
Delete

Ghost Jobs Are Wreaking Havoc On Tech Workers

2 Shares
An anonymous reader quotes a report from SFGATE: If you've recently been laid off and have started the arduous process of looking for a new job, you've probably seen them on networking platforms like LinkedIn: postings for roles that are 30 days old, maybe more, with suspiciously wide salary ranges. They usually have hundreds, or even thousands, of hopeful applicants vying for the same position, but if you do a quick cross-check and notice that the role isn't posted on the company's actual website -- or any of their social media pages -- you should probably stop drafting that cover letter, because it's possible they're not hiring at all. "Ghost jobs," or ads for positions that aren't actually open, are a common phenomenon in the tech industry, which has been plagued by layoffs and budget cuts over recent years. As unemployed workers struggle to regain their footing, recruiters and career coaches who spoke with SFGATE warned that these fake jobs posted by real companies serve multiple, sometimes insidious purposes. According to a 2024 survey from MyPerfectResume, 81% of recruiters admitted to posting ads for positions that were fake or already filled. While some respondents said employers did it to maintain a presence on job boards and build a talent pool, it's also used to commit psychological warfare: 25% said ghost jobs helped companies gauge how replaceable their employees were, while 23% said it helped make the company appear more stable during a hiring freeze. Another damning 2024 report from Resume Builder said that 62% companies posted them specifically to make their employees feel replaceable. They also made ads to "trick overworked employees" into believing that more people would be brought on to alleviate their overwhelming workload. After interviewing 1,641 hiring managers, Resume Builder researchers found that 40% of employers posted fake job listings in 2024, and that three in 10 currently had ghost jobs listed. The idea to post them mostly trickled down from HR, followed by senior management and executives, their June 2024 article continued. Though the listings were posted on multiple hiring platforms, the majority of them appeared on LinkedIn and the companies' websites. Evidence suggests this trend is taking hold throughout the Bay Area, too. A collaborative document circulating online reveals a growing list of employers accused of posting ghost jobs. Many of them, it turns out, are tech companies with offices based in California.

Read more of this story at Slashdot.

Read the whole story
Share this story
Delete
Next Page of Stories